/* Frequently-used string handling utilities
 * missing in C++ standard library, stolen from python str.
 * Limit to std::string.
 */
#pragma once

#include <string>
#include <vector>
#include <limits>


namespace evm { namespace str {

using namespace std;
using size_type = string::size_type;

constexpr const char* SPACES = " \t\n\v\f\r";

/* Return a copy of the string with all the cased characters converted to lowercase.
 * The string is passed as value, and is returned as RVO. */
string lower(const string& str);
string lower(string&& str);
string lower(const char* str);

/* Return a copy of the string with all the cased characters converted to uppercase.
 * The string is passed as value, and is returned as RVO. */
string upper(const string& str);
string upper(string&& str);
string upper(const char* str);

/* Return true if string starts with the prefix, otherwise return false.
 * With optional start, test string beginning at that position.
 * With optional end, stop comparing string at that position. */
bool startswith(const string &str, const string &prefix,
                size_type start = 0, size_type end = string::npos);
bool startswith(const string &str, const char* prefix,
                size_type start = 0, size_type end = string::npos);

/* Return true if the string ends with the specified suffix, otherwise return false.
 * With optional start, test beginning at that position.
 * With optional end, stop comparing at that position.*/
bool endswith(const string &str, const string &suffix,
              size_type start = 0, size_type end = string::npos);
bool endswith(const string &str, const char* suffix,
              size_type start = 0, size_type end = string::npos);

/* Return true if the string contains the substring, otherwise return false. */
bool contains(const string &str, const string &substr);
bool contains(const string &str, const char* substr);
bool contains(const string &str, char ch);

/* Return the number of non-overlapping occurrences of substring. */
size_type count(const string &str, const string &substr);
size_type count(const string &str, const char* substr);

/* Like std::equals, but compare case insensitively. */
bool iequals(const string &str1, const string &str2);

/* Return a string which is the concatenation of the `str_list` list,
 * separated by the `sep` string. */
template <typename Container>
string join(const string &sep, const Container &str_list) {
    return join(sep.c_str(), str_list);
}

template <typename Container>
string join(const char* sep, const Container &str_list) {
    string res;

    if (str_list.empty())
        return res;

    auto last = str_list.cend();
    --last;
    for (auto it = str_list.cbegin(); it != last; ++it) {
        res += *it;
        res += sep;
    }
    res += *last;
    return res;
}

/* Return a list of the words in the string, using `sep` as the delimiter string.
 * If maxsplit is not -1, at most maxsplit splits are done (thus, the list will have at most maxsplit+1 elements).
 * If maxsplit is not specified or -1, then there is no limit on the number of splits (all possible splits are made).
 * If sep is given, consecutive delimiters are not grouped together and are deemed to delimit empty strings.
 * Splitting an empty string with a specified separator returns [''].
 * If sep is not specified, a different splitting algorithm is applied:
 * runs of consecutive whitespace are regarded as a single separator,
 * and the result will contain no empty strings at the start or end if the string has leading or trailing whitespace.
 * Consequently, splitting an empty string or a string consisting of just whitespace with no separator returns [].
 */
vector<string> split(const string &str, int maxsplit = -1);
vector<string> split(const string &str, const string &sep, int maxsplit = -1);
vector<string> split(const string &str, const char* sep, int maxsplit = -1);

/* Slice of `str` from `start` to `stop` with step `step`.
 * If `start` or `stop` is negative, the index is relative to the end of `str`:
 * `str.size() + start` or `str.size() + stop` is substituted.
 * If `start` or `stop` is greater than str.size(), use str.size().
 * If `start` is ommitted, use 0. If `stop` is ommitted, use str.size().
 * If `start` is greater than or equal to `stop`, the slice is empty.
 * The slice is defined as the sequence of items with index `x = start + n * step` and `0 <= n < (stop-start)/step`.
 * Note, `step` cannot be 0. */
string slice(const string& str, int64_t start = 0, int64_t stop = numeric_limits<int64_t>::max(), int64_t step = 1);

/* Return a copy of the string with all occurrences of substring `old` replaced by `new_`.
 * If the optional argument `count` is given, only the first `count` occurrences are replaced. */
string replace(const string &str, const string &old, const string &new_,
               size_type count = string::npos);
string replace(const string &str, const char* old, const char* new_,
               size_type count = string::npos);

/* Return a copy of the string with the leading and trailing characters removed.
 * The `chars` argument is a string specifying the set of characters to be removed.
 * If omitted, the `chars` argument defaults to removing whitespace.
 * The `chars` argument is not a prefix or suffix; rather, all combinations of its values are stripped. */
string strip(const string& str, const string& chars);
string strip(const string& str, const char* chars = SPACES);
string strip(string&& str, const string& chars);
string strip(string&& str, const char* chars = SPACES);

/* Like `strip`, but only remove leading characters. */
string lstrip(const string& str, const string& chars);
string lstrip(const string& str, const char* chars = SPACES);
string lstrip(string&& str, const string& chars);
string lstrip(string&& str, const char* chars = SPACES);

/* Like `strip`, but only remove trailing characters. */
string rstrip(const string& str, const string& chars);
string rstrip(const string& str, const char* chars = SPACES);
string rstrip(string&& str, const string& chars);
string rstrip(string&& str, const char* chars = SPACES);

/* Return a string containing the description of the given errno value.
 * Takes care not to overwrite the actual system errno, so calling
 * strerr(errno) is valid. */
string strerror(int err);

}}
